import { InboxOutlined } from "@ant-design/icons";
import "./FileUploader.css";
import React, { useEffect, useRef, useState } from "react";
import useDrag from "./useDrag";
import { Button, message, Progress, Spin } from "antd";
import { CHUNK_SIZE, MAX_RETRIES } from "./constant";
import axiosInstance from "./axiosinstance";
import axios from "axios";
const UploadStatus = {
	NOT_STARTED: "NOT_STARTED", //初始状态，尚未开始上传
	UPLOADING: "UPLOADING", //上传中
	PAUSED: "PAUSED", //已暂停上传
};
export default function FileUploader() {
	const uploadContainerRef = useRef(null);
	const { selectedFile, filePreview, resetFileStatus } = useDrag(uploadContainerRef);
	let [uploadProgress, setUploadPeogress] = useState({});
	//控制上传状态，初始，上传中，已暂停
	const [uploadStatus, setUploadStatus] = useState(UploadStatus.NOT_STARTED);
	//存放所有上传请求的取消token
	const [cancelTokens, setCancelTokens] = useState([]);
	const [filenameWorker, setFilenameWorker] = useState(null);
	const [isCalculatingFileName, setIsCalculatingFileName] = useState(false);
	useEffect(() => {
		const filenameWorker = new Worker("/filenameWorker.js");
		setFilenameWorker(filenameWorker);
	}, []);
	const resetAllStatus = () => {
		resetFileStatus();
		setUploadPeogress({});
		setUploadStatus(UploadStatus.NOT_STARTED);
	};
	const handleUpload = async () => {
		// 上传逻辑
		if (!selectedFile) {
			message.error("您尚未选中任何文件");
			return;
		}
		setUploadStatus(UploadStatus.UPLOADING);
		//向webWorker发送一个消息，帮助计算文件对应的文件名
		filenameWorker.postMessage(selectedFile);
		setIsCalculatingFileName(true); //开始计算
		//监听webWorker发过来的消息，接收计算好的文件名
		filenameWorker.onmessage = async (event) => {
			setIsCalculatingFileName(false); //计算完成
			await uploadFile(selectedFile, event.data, setUploadPeogress, resetAllStatus, setCancelTokens);
		};
		// const filename = await getFileName(selectedFile);
		// await uploadFile(selectedFile, filename, setUploadPeogress, resetAllStatus, setCancelTokens);
	};
	//取消上传
	const pauseUpload = async () => {
		setUploadStatus(UploadStatus.PAUSED);
		cancelTokens.forEach((cancelToken) => cancelToken.cancel("暂停了上传"));
	};

	const renderButton = () => {
		switch (uploadStatus) {
			case UploadStatus.NOT_STARTED:
				return <Button onClick={handleUpload}>上传</Button>;
			case UploadStatus.UPLOADING:
				return <Button onClick={pauseUpload}>暂停</Button>;
			case UploadStatus.PAUSED:
				return <Button onClick={handleUpload}>恢复上传</Button>;
		}
	};
	const renderProgress = () => {
		if (uploadStatus !== UploadStatus.NOT_STARTED) {
			let totalProgress = renderTotalProgress();
			let chunkProgress = Object.keys(uploadProgress).map((chunkName, index) => (
				<div>
					<span>切片{index}:</span>
					<Progress percent={uploadProgress[chunkName]} />
				</div>
			));
			return (
				<>
					{totalProgress}
					{chunkProgress}
				</>
			);
		}
	};
	const renderTotalProgress = () => {
		const percents = Object.values(uploadProgress);
		const totalPercent = Math.round(percents.reduce((acc, curr) => acc + curr, 0) / percents.length);
		return (
			<div>
				<span>总进度</span>
				<Progress percent={totalPercent} />
			</div>
		);
	};
	return (
		<>
			<div
				className="upload-container"
				ref={uploadContainerRef}>
				{renderFilePreview(filePreview)}
			</div>
			{renderButton()}
			{/* {renderTotalProgress()} */}
			{isCalculatingFileName && <Spin tip={<span>正在计算文件名...</span>}></Spin>}
			{renderProgress()}
		</>
	);
}
//显示文件预览
function renderFilePreview(filePreview) {
	const { url, type } = filePreview;
	if (url) {
		if (type.startsWith("video/")) {
			return (
				<video
					src={url}
					alt="preview"
					controls //播放控制条
				/>
			);
		} else if (type.startsWith("image/")) {
			return (
				<img
					src={url}
					alt="preview"
				/>
			);
		} else {
			return url;
		}
	} else {
		return <InboxOutlined />;
	}
}

//实现切片上传大文件
//file文件   filename文件名
async function uploadFile(file, filename, setUploadPeogress, resetAllStatus, setCancelTokens, retryCount = 0) {
	const { needUpload, uploadedChunkList } = await axiosInstance.get(`/verify/${filename}`);
	if (!needUpload) {
		message.success("文件已存在，秒传成功");
		return resetAllStatus();
	}
	//将文件进行切片
	const chunks = createFileChunks(file, filename);
	const newCancelTokens = [];

	// console.log(chunks);

	//实现并行上传
	const requests = chunks.map(({ chunk, chunkFileName }) => {
		const cancelToken = axios.CancelToken.source();
		newCancelTokens.push(cancelToken);
		//以后向服务器发送的数据可能不是完整的分片数据
		//判断当前分片是否已上传过服务器
		const existingChunk = uploadedChunkList.find((uploadedChunk) => {
			return uploadedChunk.chunkFileName == chunkFileName;
		});
		//如果存在，说明此分片已上传过
		if (existingChunk) {
			//获取上传过的分片大小
			const uploadedSize = existingChunk.size;
			//从chunk中截取，过滤掉已经上传过的大小，得到剩下需要上传的内容
			const remainingChunk = chunk.slice(uploadedSize);
			//如果剩下的内容为空，说明此分片已上传过
			if (remainingChunk.size === 0) {
				setUploadPeogress((prevProgress) => ({
					...prevProgress, //老的进度
					[chunkFileName]: 100,
				}));
				return Promise.resolve();
			}
			//如果还有剩下的数据，继续上传
			//如果100字节，已经传了60字节，暂停之后恢复上传，则从60开始上传剩下的40
			setUploadPeogress((prevProgress) => ({
				...prevProgress, //老的进度
				[chunkFileName]: (uploadedSize * 100) / chunk.size,
			}));
			return createRequest(filename, chunkFileName, remainingChunk, setUploadPeogress, cancelToken, uploadedSize, chunk.size);
		} else {
			return createRequest(filename, chunkFileName, chunk, setUploadPeogress, cancelToken, 0, chunk.size);
		}
	});
	setCancelTokens(newCancelTokens);
	try {
		//并行上传
		await Promise.all(requests);
		//等全部的分片上传完了，会向服务器发送一个合并文件的请求
		await axiosInstance.get(`/merge/${filename}`);
		message.success("文件上传完成");
		resetAllStatus(); //重置状态
	} catch (error) {
		//用户主动点击暂停按钮，暂停上传
		if (axios.isCancel(error)) {
			console.log("上传暂停", error);
			message.warning("上传暂停");
		} else {
			if (retryCount < MAX_RETRIES) {
				console.log(`上传出错，重试中...`);
				uploadFile(file, filename, setUploadPeogress, resetAllStatus, setCancelTokens, (retryCount +1));
			} else {
				console.log("上传出错", error);
				message.error("上传出错");
			}
		}
	}
}
function createFileChunks(file, filename) {
	let chunks = [];
	//计算一共切成多少片
	let count = Math.ceil(file.size / CHUNK_SIZE);
	for (let i = 0; i < count; i++) {
		let chunk = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
		chunks.push({
			chunk,
			chunkFileName: `${filename}-${i}`,
		});
	}
	return chunks;
}

function createRequest(filename, chunkFileName, chunk, setUploadPeogress, cancelToken, start, totalSize) {
	return axiosInstance.post(`/upload/${filename}`, chunk, {
		headers: {
			//这个请求头告诉服务器请求体是一个二进制格式，是一个字节流
			"Content-Type": "application/octet-stream", //文件流只能上传二进制文件
		},
		params: {
			//此查询参数拼接到url地址里
			chunkFileName,
			start, //将写入文件的起始位置也作为查询，参数发送给服务器
		},
		onUploadProgress: (progressEvent) => {
			//上传进度
			//progressEvent.loaded本次上传成功的字节数+start上次上传成功的字节数/总字节数
			const percentCompleted = Math.round(((progressEvent.loaded + start) * 100) / totalSize);
			setUploadPeogress((prevProgress) => ({
				...prevProgress, //老的进度
				[chunkFileName]: percentCompleted,
			}));
		},
		cancelToken: cancelToken.token, //axios的cancelToken来实现取消上传
	});
}
